package com.cloud.charts4a.chart;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import com.cloud.charts4a.chart.attrib.AbstractXAxisAttribs;
import com.cloud.charts4a.chart.attrib.YAxisAttribs;
import com.cloud.charts4a.chart.attrib.YRangeHighLightAttribs;
import com.cloud.charts4a.chart.render.TimeChartRender;
import com.cloud.charts4a.data.TimeChartData;
import com.cloud.charts4a.data.ChartDataSeries;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Time Chart<p/>
 * 
 * @version
 * <ol>
 * 	<li>2010/07/09 CloudTu：First Release</li>
 * </ol>
 * 
 * @author Cloud Tu
 */
public class TimeChart extends View {	
	/**
	 * 手指壓下折線點時的感應區塊半徑大小
	 */
	private float touchDotRadius;	
	
	/**
	 * 判斷"對圖表進行左右拖拉"的最小區間值
	 */
	private float minDragChartToLeftOrRightRange;
	
	/**
	 * 一單位dip(Density-independent pixel)轉換成Pixel後的值
	 */
	private float oneDipToPixel;	
	
	private XAxisAttribs xAxisAttribs;
	private YAxisAttribs yAxisAttribs;
	private YRangeHighLightAttribs yRangeHighLightAttribs;
	private List<ChartDataSeries<TimeChartData>> chartDataSeriesList = new ArrayList<ChartDataSeries<TimeChartData>>();
	private TimeChartData clickedChartData;	//被點擊的折線點資料
	private float touchStartXPosition;	//x-axis position of start touch event 
	private float touchEndXPosition;	//x-axis position of end touch event
	private IAfterDragChartListener afterDragChartListener;
	private boolean isShowXUnitDesc = true;		//是否要顯示X軸的單位說明(預設為顯示)
	private boolean isShowYUnitDesc = true;		//是否要顯示Y軸的單位說明(預設為顯示)		
	private boolean isShowLegend = true;		//是否要顯示圖例(預設為顯示)
	private boolean isOnlyShowDot = false;		//是否只顯示折線點(預設是顯示折線與折線點)		
	private SimpleDateFormat tipDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");	//折線點被點擊時，顯示出來提示文字裡，日期部份的日期格式物件(預設為"yyyy/MM/dd HH:mm:ss")
	
	private TimeChartRender render;
	
	public TimeChart(Context context) {
		super(context);
	}
	
	public TimeChart(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);	
	}

	public TimeChart(Context context, AttributeSet attrs) {
		super(context, attrs);	
	}	
	
	/**
	 * Initial chart background
	 * 
	 * @param xAxisAttribs	XAxisAttribs
	 * @param yAxisAttribs	YAxisAttribs
	 */
	public void initChartBackground(XAxisAttribs xAxisAttribs,YAxisAttribs yAxisAttribs){
		this.xAxisAttribs = xAxisAttribs;
		this.yAxisAttribs = yAxisAttribs;
		clickedChartData = null;
		oneDipToPixel = getResources().getDisplayMetrics().density;
		touchDotRadius = 15 * oneDipToPixel;
		minDragChartToLeftOrRightRange = 50 * oneDipToPixel;
	}
	
	/**
	 * 設定Chart裡的數據資料
	 * 
	 * @param chartDataSeriesList	TimeChartDataSeries list
	 */
	public void setChartContent(List<ChartDataSeries<TimeChartData>> chartDataSeriesList){
		this.chartDataSeriesList = chartDataSeriesList;
		clickedChartData = null;
	}
	
	/**
	 * 設定Chart裡要被Hightlight區塊的各項屬性
	 * 
	 * @param yRangeHighLightAttribs	Y-Axis range highlight attributes
	 */
	public void setYRangeHighLightAttribs(YRangeHighLightAttribs yRangeHighLightAttribs){
		this.yRangeHighLightAttribs = yRangeHighLightAttribs;
		clickedChartData = null;
	}	
	
	/**
	 * 設定Chart被左右拖拉後所觸發的Listener
	 * 
	 * @param afterDragChartListener	圖表被往左或往右拖拉後所觸發的Listener
	 */
	public void setAfterDragChartListener(IAfterDragChartListener afterDragChartListener){
		this.afterDragChartListener = afterDragChartListener;
		clickedChartData = null;
	}
	
	/**
	 * 設定"折線點被點擊時，顯示出來提示文字裡，日期部份的顯示格式"
	 * 
	 * @param tipDateFormat		日期格式字串(請參照JavaSE中的SimpleDateFormat說明)(e.g. "yyyy/MM/dd HH:mm:ss")
	 */
	public void setTipDateFormat(String dateFormatStr){
		tipDateFormat = new SimpleDateFormat(dateFormatStr);
		clickedChartData = null;
	}
	
	/**
	 * 設定是否要顯示Chart裡X軸的單位說明
	 * 
	 * @param isShowXUnitDesc	是否要顯示Chart裡X軸的單位說明
	 */
	public void setShowXUnitDesc(boolean isShowXUnitDesc){
		this.isShowXUnitDesc = isShowXUnitDesc;
		clickedChartData = null;
	}	
	
	/**
	 * 設定是否要顯示Chart裡Y軸的單位說明
	 * 
	 * @param isShowYUnitDesc	是否要顯示Chart裡Y軸的單位說明
	 */
	public void setShowYUnitDesc(boolean isShowYUnitDesc){
		this.isShowYUnitDesc = isShowYUnitDesc;
		clickedChartData = null;
	}		
	
	/**
	 * 設定是否要顯示Chart的圖例
	 * 
	 * @param isShowLegend	是否要顯示Chart的圖例
	 */
	public void setShowLegend(boolean isShowLegend){
		this.isShowLegend = isShowLegend;
		clickedChartData = null;
	}
	
	/**
	 * 設定是否只顯示Chart上的折線點
	 * 
	 * @param isOnlyShowDot	是否只顯示Chart上的折線點
	 */	
	public void setOnlyShowDot(boolean isOnlyShowDot){
		this.isOnlyShowDot = isOnlyShowDot;
		clickedChartData = null;
	}
	
	/**
	 * Draw chart
	 */
	public void drawChart(){
		invalidate();		
	}	
	
	/**
	 * Update chart content and redraw chart
	 * 
	 * @param chartDataSeriesList	TimeChartDataSeries list
	 */
	public void updateChartContentAndReDrawChart(List<ChartDataSeries<TimeChartData>> chartDataSeriesList){
		this.chartDataSeriesList = chartDataSeriesList;
		clickedChartData = null;
		invalidate();		
	}
	
	/**
	 * 將圖表往左拖拉
	 */
	private void dragChartToLeft(){	
		Date minXDate = xAxisAttribs.getMinXValue();
		Date maxXDate = xAxisAttribs.getMaxXValue();
		long moveInterval = maxXDate.getTime() - minXDate.getTime();	//位移的X軸時間(單位為millisecond)
		minXDate.setTime(minXDate.getTime() + moveInterval);
		maxXDate.setTime(maxXDate.getTime() + moveInterval);
		
		if(afterDragChartListener != null){
			chartDataSeriesList = afterDragChartListener.reCreateChartContent(minXDate, maxXDate);
		}
		
		clickedChartData = null;
		invalidate();
	}
	
	/**
	 * 將圖表往右拖拉
	 */	
	private void dragChartToRight(){	
		Date minXDate = xAxisAttribs.getMinXValue();
		Date maxXDate = xAxisAttribs.getMaxXValue();
		long moveInterval = maxXDate.getTime() - minXDate.getTime();	//位移的X軸時間(單位為millisecond)		
		minXDate.setTime(minXDate.getTime() - moveInterval);
		maxXDate.setTime(maxXDate.getTime() - moveInterval);
		
		if(afterDragChartListener != null){
			chartDataSeriesList = afterDragChartListener.reCreateChartContent(minXDate, maxXDate);
		}
		
		clickedChartData = null;
		invalidate();
	}	
	
	/**
	 * 是否執行了"圖表往左或往右拖拉"的動作
	 * 
	 * @param event	MotionEvent
	 * 
	 * @return		true:圖表被往左或往右拖拉 / false:圖表沒被往左或往右拖拉
	 */
	private boolean isDoDragChartToLeftOrRight(MotionEvent event){			
		if(event.getAction() == MotionEvent.ACTION_DOWN) {
			touchStartXPosition = event.getX();
		}
		
		if(event.getAction() != MotionEvent.ACTION_DOWN){
			touchEndXPosition = event.getX();
		}		

		if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL){			
			if(touchStartXPosition - touchEndXPosition > minDragChartToLeftOrRightRange){	
				dragChartToLeft();
				return true;
			}
			else if(touchEndXPosition - touchStartXPosition > minDragChartToLeftOrRightRange){
				dragChartToRight();
				return true;			
			}			
		}		
		
		return false;
	}	
	
	/**
	 * 是否執行了"顯示折線點裡的提示文字"的動作
	 * 
	 * @param event	MotionEvent
	 * 
	 * @return		true:執行了"顯示折線點裡的提示文字"的動作 / false:未執行"顯示折線點裡的提示文字"的動作
	 */
	private boolean isDoShowDataTipOnChart(MotionEvent event){
		if(event.getAction() == MotionEvent.ACTION_DOWN && chartDataSeriesList != null) {						
			if(clickedChartData != null){
				invalidate(clickedChartData.getTipRect());
			}			
						
			float startXPosition = event.getX() - touchDotRadius;
			float endXPosition = event.getX() + touchDotRadius;
			float startYPosition = event.getY() - touchDotRadius;
			float endYPosition = event.getY() + touchDotRadius;			
			
			for (ChartDataSeries<TimeChartData> chartDataSeries : chartDataSeriesList) {
				for(int i=0;i<chartDataSeries.size();i++){
					TimeChartData chartData = chartDataSeries.getChartData(i);
					if(chartData.getXPosition() != null &&
					   chartData.getXPosition() >= startXPosition && 
					   chartData.getXPosition() <= endXPosition &&
					   chartData.getYPosition() >= startYPosition &&
					   chartData.getYPosition() <= endYPosition){										
						if(chartData.isShowTip() == true){
							chartData.setShowTip(false);
						}
						else{
							chartData.setShowTip(true);
						}
						
						clickedChartData = chartData;
						invalidate(clickedChartData.getTipRect());
					}
					else{
						chartData.setShowTip(false);
					}
				}
			}			
			return true;			
		}
		return false;
	}	
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {											
		if(isDoDragChartToLeftOrRight(event)){
			return true;
		}		
		
		if(isDoShowDataTipOnChart(event)){
			return true;
		}				
				
//XXX	直接回傳true,不再回傳super.onTouchEvent()。用以解決觸摸螢幕並進行拖放時，總是回傳MotionEvent.ACTION_DOWN訊號
//		的怪現象。這問題有可能是Android本身的bug。
//		return super.onTouchEvent(event);		
		return true;	
	}		
	
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);														

		if(clickedChartData != null && clickedChartData.isShowTip()){
			render.drawChart();
			render.drawTipOnChartWhenDotIsClicked(clickedChartData);
		}
		else{
			render = new TimeChartRender(canvas, getWidth(), getHeight(),
										 oneDipToPixel,
										 xAxisAttribs, yAxisAttribs,
										 yRangeHighLightAttribs, chartDataSeriesList,
										 tipDateFormat,isOnlyShowDot,isShowLegend,
										 isShowXUnitDesc,isShowYUnitDesc);
			render.drawChart();				
		}			
	}	
	
	/**
	 * 圖表被往左或往右拖拉後所觸發的Listener<p/>
	 * 
	 * @version
	 * <ol>
	 * 	<li>2010/07/09 CloudTu：First Release</li>
	 * </ol>
	 * 
	 * @author Cloud Tu
	 */	
	public static interface IAfterDragChartListener{
		/**
		 * 圖表往左或往右拖拉後，重新產生圖表內的資料
		 * 
		 * @param minXDate	正在顯示的圖表中,X軸最小值的日期
		 * @param maxXDate	正在顯示的圖表中,X軸最大值的日期
		 * 
		 * @return	圖表內的資料
		 */
		List<ChartDataSeries<TimeChartData>> reCreateChartContent(Date minXDate,Date maxXDate);				
	}
	
	/**
	 * X-Axis attributes in chart<p/>
	 * 
	 * @version
	 * <ol>
	 * 	<li>2010/07/09 CloudTu：First Release</li>
	 * </ol>
	 * 
	 * @author Cloud Tu
	 */	
	public static class XAxisAttribs extends AbstractXAxisAttribs<Date, Date>{
		private String dateFormat;	//時間日期格式(e.g. "yyyy/MM/dd HH:mm:ss")
		
		/**
		 * X-Axis attributes in chart
		 * 
		 * @param minXValue			X軸裡起始的時間
		 * @param maxXValue			X軸裡結束的時間
		 * @param dateFormat		時間日期格式(e.g. "yyyy/MM/dd HH:mm:ss")
		 * @param xValueInterval	相臨二個X軸日期間隔儿分鐘
		 * @param xUnitDesc			X軸的單位說明
		 */
		public XAxisAttribs(Date minXValue, Date maxXValue,String dateFormat,int xValueInterval,String xUnitDesc) {			
			boolean isSetHourToZero = true;
			boolean isSetMinuteToZero = true;
			if(dateFormat.contains("h") || dateFormat.contains("H")){
				isSetHourToZero = false;
			}
			if(dateFormat.contains("m")){
				isSetMinuteToZero = false;
			}			
			
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(minXValue);
			if(isSetHourToZero == true){
				calendar.set(Calendar.HOUR_OF_DAY, 0);				
			}
			if(isSetMinuteToZero == true){
				calendar.set(Calendar.MINUTE, 0);				
			}
			calendar.set(Calendar.SECOND, 0);
			calendar.set(Calendar.MILLISECOND, 0);				
			this.minXValue = calendar.getTime();	//X-Axis的起始時間,秒的部份都設成0
			
			calendar.setTime(maxXValue);
			if(isSetHourToZero == true){
				calendar.set(Calendar.HOUR_OF_DAY, 0);				
			}
			if(isSetMinuteToZero == true){
				calendar.set(Calendar.MINUTE, 0);				
			}
			calendar.set(Calendar.SECOND, 0);
			calendar.set(Calendar.MILLISECOND, 0);				
			this.maxXValue = calendar.getTime();	//X-Axis的結束時間,秒的部份都設成0		
			
			this.dateFormat = dateFormat;			
			this.xValueInterval = xValueInterval;
			this.xUnitDesc = xUnitDesc;
		}

		/**
		 * 取得時間日期格式(e.g. "yyyy/MM/dd HH:mm:ss")
		 * 
		 * @return	時間日期格式(e.g. "yyyy/MM/dd HH:mm:ss")
		 */			
		public String getDateFormat() {
			return dateFormat;
		}					
	}			
}
